java反射机制中class.forName和classloader的区别

类加载机制

在这里插入图片描述

大致分为5个阶段:
(1)加载:java类运行时候会生成一个class字节码文件,加载的过程就是去我们的操作系统寻找这个class文件。
(2)链接:这个过程就是把class文件加载到java虚拟机。
(3)初始化:在虚拟机中根据class文件进行初始化。
(4)使用:这个过程大家都明白。
(5)卸载:使用完了,java虚拟机进行清理。对于class.forName和classloader来说针对的就是第一个过程,也就是加载过程。不过这俩虽然有一定的相似性,但是区别还是挺大的。
详情可看另一边博客深入讲解jvm原理

代码举例

我们使用代码,先看看如何使用。注意包的范围,避免加载不了。第一步:定义ClassDemo类

public class ClassDemo {
    private static int a = 1;
    private static int b = inttB();





    {
        System.out.println("这是普通代码块");
    }
    static{
        System.out.println("这是静态代码块");
    }

    private static int inttB() {
        System.out.println("静态成员变量初始化时调通了静态方法");
        return 6;
    }

    private static void test() {
        System.out.println("初始化时没有执行静态方法");
    }
}

测试

public class ClassLoaderTest {

    public static void main(String[] args) throws ClassNotFoundException {
        String classDemo = "ClassDemo";
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        System.out.println("classLoader test  ------------------------------------");
        Class<?> aClass = loader.loadClass(classDemo);
        System.out.println(aClass.getName());
        System.out.println("我是一条分割线--------------------------------");
        Class<?> aClass1 = Class.forName(classDemo);
        System.out.println("Class.forName test------------------------------------");
        System.out.println(aClass1.getName());
    }
}

结果

在这里插入图片描述

可以看出来前者只是加载了类到jvm,而后者还会加载静态成员变量和静态代码块

总结

1、class.forName

前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。注意这里的静态块指的是在类初始化时的一些数据。但是classloader却没有,想要弄清楚这个原因,还是直接到源码中看看。

@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    Class<?> caller = null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        // Reflective call to get caller class is only needed if a security manager
        // is present.  Avoid the overhead of making this call otherwise.
        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);
            }
        }
    }
    return forName0(name, initialize, loader, caller);
}

在这个源码中我们会发现,其实底层真正实现的是forName0方法,那这几个参数又是什么意思呢?
(1)className:表示我们要加载的类名
(2)true:指Class被加载后是不是必须被初始化。 不初始化就是不执行static的代码即静态代码,在这里默认为true,也就是默认实现类的初始化。
(3)ClassLoader.getClassLoader(caller):表示类加载器,到这你会发现forNanme其实也是使用的ClassLoader类加载器加载的。
(4)caller:指定类加载器。所以,在这里你可以指定是否在class加载后被初始化。而且底层还是使用的classloader加载的。

2、classloader

在上面的案例中我们发现,classloader并没有初始化静态块,原因最好还是到源码中看。首先我们先进入到loadclass方法中的源码。

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

这一步看起来还看不明白,没关系这里真正实现的是内部的loadclass,我们再跟进去看看。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    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();
                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;
    }
}

protected Object getClassLoadingLock(String className) {
    Object lock = this;
    if (parallelLockMap != null) {
        Object newLock = new Object();
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}

这个才是真正实现的方法,在这里的步骤其实很简单,大致流程是先判断class是否已经被加载,如果被加载了那就重新加载,如果没有加载那就使用双亲委派原则加载。加载的时候并没有指定是否要进行初始化。

所以他们的区别基本上很少,总结一下:

(1)class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。当然还可以指定是否执行静态块。
(2)classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

展开阅读全文
©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读