JAVA中Class.forName()和ClassLoader.loadClass()对比

先上代码抛出问题:

package com.myClassLoaderTest;

/**
 * 用于对比的测试类
 * 1.该类中有静态代码块。
 * 2.测试Class.forName和classLoader的不同
 *
 * 测试结果
 *
 * @ Author:  liu xuanjie
 * @ Date:    2020/7/29
 */
public class TestObject
{
    public static int value = 1;

    public TestObject()
    {
        System.out.println("成功进入到TestClass的无参构造方法中!");
    }

    static
    {
        System.out.println("成功进入到TestClass的静态代码块中!");
    }

    public static void method()
    {
        System.out.println("成功进入到TestClass的静态方法中!");
    }
}

上述Class为一个测试所用类,,,其中包含的组件有,静态成员变量,无参重写构造方法,静态代码块,静态方法。

package com.myClassLoaderTest;

/**
 * 测试类
 *
 * @ Author:  liu xuanjie
 * @ Date:    2020/7/29
 */
public class TestClass 
{
    /**
     * 主函数,程序入口
     */
    public static void main(String[] args) throws Exception
    {
        String classNameHasPackage = "com.myClassLoaderTest.TestObject";
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();


        System.out.println("================下面是Clazz1的测试结果=====================");
        Class clazz1 = Class.forName(classNameHasPackage);

        System.out.println("================下面是Clazz2的测试结果==================================");
        Class clazz2 = Class.forName(classNameHasPackage, false, classLoader);

        System.out.println("================下面是Clazz3的测试结果=====================================");
        Class clazz3 = classLoader.loadClass(classNameHasPackage);
    }
}

这是一个测试类,,,主要测试Class.forName()和ClassLoader.loadClass()的区别。

看测试结果:

问题分析:

首先,说点预备知识,JVM把一个编译好的(.class)文件转换成内存中可使用创建的Class实例需要经过一系列过程。

分别为:::加载,验证,准备,解析,初始化。。。完成这五步之后,就可以实例化了。。。。

其中验证,准备,解析,又被统称为连接过程。

(有兴趣的同学自行参考相关知识,我的其它博客中也有简单介绍,但大体都是《深入理解JAVA虚拟机》一书中的叙述)

而我们要说的就是上述五个过程中的加载阶段。。。。

总结加载这一步的主要意义:::就是生成了一个类对应的java.lang.Class对象。。。

而我们的主角就是做的这件事情:

Class.forName()和ClassLoader.loadClass()都是根据一个类的全限定名,根据(.class)文件的字节码获取到对应的java.lang.Class对象。。。而他们之间的本质区别就是,,,结束的时期不一致。。。。

本质区别:

根据之前的测试用例,我们不难发现,

1. Class.forName()方法,最终执行到了静态代码块。。。。

2.Class.forName(name, false, loader)方法,没有执行到静态代码块。。。

3.ClassLoader.loadClass()方法,没有执行到静态代码块。。。。

所以到此,问题可以得到结论了:我们知道,在类的整个加载(不是上述五步中所说的加载,上述五步骤的总和)过程中,能够访问到静态代码块(注意这里说的不是静态方法,是静态代码块)的步骤就是“初始化”的这一步,这一步形成的<Clinit>()方法,自动收集静态代码块并执行,,,所以说Class.forName()这个方法下的过程已经完成了初始化。。。。

而相应的后续两种方法都是仅仅完成了第一步的加载过程,返回了一个类对应的Class实例,提供访问类信息的方法接口。

深入分析:

1.forName(name, boolean,Loader)方法

我们来看,这个方法区别于第一种方式的这个boolean是干嘛的,找到源码:

对比这两个方法,最终都是调用了底层的 forName0()方法,不同的就是这个boolean initialize布尔参数的不同。。

我们来看这个参数的官方解释:::

 

不出我们所料,,,加上这个boolean标识位控制的就是class是否被初始化。。。。

而测试用例中第一次的forName(name)方法,没有设置这个boolean值,但是看上述源码,其默认了true。。。

2.ClassLoader.loadClass(name)方法

首先该方法去调用了双参数的loadClass()方法,,又是多了一个boolean值,来看解释

意思是,,,如果该boolean标识变量为true的话,就会解析,当然了false就是不会。。。

而上述源码中,很明显,默认的是false。。。

并且还有一个小细节,双参数的loadClass()方法是 protected的,外部保护不可访问的。。。。

猜你喜欢

转载自blog.csdn.net/romantic_jie/article/details/107659868