zoukankan      html  css  js  c++  java
  • Class.forName() 与 ClassLoader.loadClass()的区别

        看到一个面试题,说说Class.forName() 与 ClassLoader.loadClass()的区别,特意记录一下,方便后续查阅。
        在我们写java代码时,通常用这两种方式来动态加载一个java类,它们是Class.forName() 与 ClassLoader.loadClass()。 但是这两个方法之间也是有一些细微的差别,下面通过写代码实践的方式来看看结果。

    一、Class.forName() 方式

    (1)理论

        查看Class类代码里面的具体实现可知,实质上这个方法是调用原生的方法如下:
           /** Called after security check for system loader access checks have been made. */
        private static native Class<?> forName0(String name, boolean initialize,
                                                ClassLoader loader,
                                                Class<?> caller)throws ClassNotFoundException;
            形式上类似于Class.forName(name,true,currentLoader)。综上所述,Class.forName 如果调用成功后:
        • 将一个Java类被有效得加载到内存中;
        • 类默认会被初始化(initialize 这个参数传的是true),即执行内部的静态块代码以及保证静态属性被初始化;
        • 默认会使用当前的类加载器来加载对应的类。

    (2)代码说明

            下面通过代码来说明
            参考代码:
    public class TestClass {
        public static void main(String[] args) throws ClassNotFoundException {
            System.out.println("Class.forName 方式加载类--->start");
            Class.forName("com.jwx.digital.client.AClass").getClass();
            System.out.println("Class.forName 方式加载类--->end");
        }
    }
    
    class AClass {
        static {
            System.out.println("AClass初始化");
            System.out.println("=====AClass静态代码快执行=====");
        }
    }
        运行结果:
    Class.forName 方式加载类--->start
    AClass初始化
    =====AClass静态代码快执行=====
    Class.forName 方式加载类--->end

    二、ClassLoader.loadClass方式

    (1)理论

            采用这种方式的类加载策略,由于双亲委派模型的存在,最终都会将类的加载任务交付给Bootstrap ClassLoader进行加载。跟踪源代码,最终会调用原生方法:
        // return null if not found
        private native Class<?> findBootstrapClass(String name);
            与此同时,与Class.forName()方式的最本质的不同是,类不会被初始化,只有显式调用才会进行初始化。综上所述,ClassLoader.loadClass 如果调用成功后:
        • 将一个java类加载到内存中;
        • 类不会被初始化,只有在之后被第一次调用时类才会被初始化;因为虚拟机规范规定了,当遇到new、getstatic、putstatic或invokestatic这4条字节码指令是,如果没有进行过初始化则需要先触发初始化。

    (2)代码说明

        下面通过代码来说明
            参考代码:
    public class TestClass {
        public static void main(String[] args) throws Exception {
            System.out.println("ClassLoader 方式加载类--->start");
            Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.jwx.digital.client.AClass");
            System.out.println("ClassLoader 方式加载类--->end");
            /* 虽然上面没有进行类的初始化,
             但是虚拟机规范规定了,当遇到new、getstatic、putstatic或invokestatic这4条字节码指令是,如果没有进行过初始化则需要先触发初始化。
             生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、
             读取或者设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。*/
            // 因此我们实例化这个class时,就会调用初始化
            System.out.println("我要实例化这个类了+++++");
            aClass.newInstance();
        }
    }
    
    class AClass {
        static {
            System.out.println("AClass初始化");
            System.out.println("=====AClass静态代码快执行=====");
        }
    }
        运行结果:
    ClassLoader 方式加载类--->start
    ClassLoader 方式加载类--->end
    我要实例化这个类了+++++
    AClass初始化
    =====AClass静态代码快执行=====

    三、总结

        (1)Class.forName的形式加载类时,默认会执行类的初始化
        (2)ClassLoader.loadClass的方式加载类时,类不会被初始化,只有显式调用才会进行初始化。可以用来延迟加载(采用ClassLoader进行懒加载,就不会调用类的静态代码快,实现延迟加载)

    四、补充

     当一个类被主动使用时,Java虚拟机就会对其初始化,如下六种情况为主动使用

      1. 当创建某个类的新实例时(如通过new或者反射,克隆,反序列化等)
      2. 当调用某个类的静态方法时
      3. 当使用某个类或接口的静态字段时
      4. 当调用Java API中的某些反射方法时,比如类Class中的方法,或者java.lang.reflect中的类的方法时
      5. 当初始化某个子类时
      6. 当虚拟机启动某个被标明为启动类的类(即包含main方法的那个类)




  • 相关阅读:
    c语言知识
    数字地与模拟地
    C语言实现顺序表(增删)
    传统数据库、Nosql数据库与云数据库区别?
    大数据处理架构如何
    warning: implicit declaration of function 'func1' [-Wimplicit-function-declaration]
    window10创建virtualenv虚拟环境
    二叉树的实现以及三种遍历方法--代码
    损失函数--KL散度与交叉熵
    市场回测与对冲套利
  • 原文地址:https://www.cnblogs.com/zeng1994/p/81976a2ea470861e34a4223b7561a970.html
Copyright © 2011-2022 走看看