看到一个面试题,说说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;
4
 
1
       /** Called after security check for system loader access checks have been made. */
2
    private static native Class<?> forName0(String name, boolean initialize,
3
                                            ClassLoader loader,
4
                                            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静态代码快执行=====");
}
}
14
 
1
public class TestClass {
2
    public static void main(String[] args) throws ClassNotFoundException {
3
        System.out.println("Class.forName 方式加载类--->start");
4
        Class.forName("com.jwx.digital.client.AClass").getClass();
5
        System.out.println("Class.forName 方式加载类--->end");
6
    }
7
}
8

9
class AClass {
10
    static {
11
        System.out.println("AClass初始化");
12
        System.out.println("=====AClass静态代码快执行=====");
13
    }
14
}
    运行结果:
Class.forName 方式加载类--->start
AClass初始化
=====AClass静态代码快执行=====
Class.forName 方式加载类--->end
4
 
1
Class.forName 方式加载类--->start
2
AClass初始化
3
=====AClass静态代码快执行=====
4
Class.forName 方式加载类--->end

二、ClassLoader.loadClass方式

(1)理论

        采用这种方式的类加载策略,由于双亲委派模型的存在,最终都会将类的加载任务交付给Bootstrap ClassLoader进行加载。跟踪源代码,最终会调用原生方法:
    // return null if not found
private native Class<?> findBootstrapClass(String name);
2
 
1
    // return null if not found
2
    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静态代码快执行=====");
}
}
21
 
1
public class TestClass {
2
    public static void main(String[] args) throws Exception {
3
        System.out.println("ClassLoader 方式加载类--->start");
4
        Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.jwx.digital.client.AClass");
5
        System.out.println("ClassLoader 方式加载类--->end");
6
        /* 虽然上面没有进行类的初始化,
7
         但是虚拟机规范规定了,当遇到new、getstatic、putstatic或invokestatic这4条字节码指令是,如果没有进行过初始化则需要先触发初始化。
8
         生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、
9
         读取或者设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。*/
10
        // 因此我们实例化这个class时,就会调用初始化
11
        System.out.println("我要实例化这个类了+++++");
12
        aClass.newInstance();
13
    }
14
}
15

16
class AClass {
17
    static {
18
        System.out.println("AClass初始化");
19
        System.out.println("=====AClass静态代码快执行=====");
20
    }
21
}
    运行结果:
ClassLoader 方式加载类--->start
ClassLoader 方式加载类--->end
我要实例化这个类了+++++
AClass初始化
=====AClass静态代码快执行=====
5
 
1
ClassLoader 方式加载类--->start
2
ClassLoader 方式加载类--->end
3
我要实例化这个类了+++++
4
AClass初始化
5
=====AClass静态代码快执行=====

三、总结

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

四、补充

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

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


Class.forName() 与 ClassLoader.loadClass()的区别的更多相关文章

  1. 015 反射中的 Class.forName() 与 ClassLoader.loadClass() 的区别

    作者:nnngu GitHub:https://github.com/nnngu 博客园:http://www.cnblogs.com/nnngu 简书:https://www.jianshu.com ...

  2. 反射中Class.forName()和ClassLoader.loadClass()的区别

    一 Java类装载过程 装载:通过累的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lang.class对象: 链接:执行下面的校验.准备和解析步骤,其 ...

  3. Class.forName和ClassLoader.loadClass的区别

    Class的装载分了三个阶段,loading,linking和initializing,分别定义在The Java Language Specification的12.2,12.3和12.4. Cla ...

  4. 反射中的 Class.forName() 与 ClassLoader.loadClass() 的区别

    在Java中,类加载器把一个类加载进Java虚拟机中,要经过三个步骤来完成:加载.链接和初始化,其中链接又可以分成验证.准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下 ...

  5. Class.forName和ClassLoader.loadClass的区别(转载)

    Class的装载分了三个阶段,loading,linking和initializing,分别定义在The Java Language Specification的12.2,12.3和12.4.Clas ...

  6. Class.forName和ClassLoader.loadClass区别(转)

    Java中class是如何加载到JVM中的:1.class加载到JVM中有三个步骤    装载:(loading)找到class对应的字节码文件.    连接:(linking)将对应的字节码文件读入 ...

  7. 简单总结Class.forName("").newinstance()和new()以及classLoader.loadClass("")的区别

    文章目录 背景 三种方法简单介绍 Class.forName("").newinstance()方式 new方式 classLoader.loadClass("" ...

  8. java两种反射的区别 - Class.forName()和ClassLoader.loadClass()

    在理解这两种反射机制之前,需要弄清楚java类的加载机制. 装载:通过类的全限定名获取二进制字节流(二进制的class文件),将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lan ...

  9. Class.forName和ClassLoader.loadClass的比较【转载】

    Class的装载分了三个阶段,loading,linking和initializing,分别定义在The Java Language Specification的12.2,12.3和12.4.Clas ...

随机推荐

  1. LeetCode 669. Trim a Binary Search Tree修剪二叉搜索树 (C++)

    题目: Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so th ...

  2. LeetCode 53. Maximum Subarray最大子序和 (C++)

    题目: Given an integer array nums, find the contiguous subarray (containing at least one number) which ...

  3. OSS文档1

    简介:     OSS 对象存储   用于单独存储文件视频音频类等文件   上传方式: 普通上传: 单文件普通上传 分片上传: 文件切片后上传,完成后组合,适合大文件,弱网络 追加上传: 流文件上传, ...

  4. SQL Server 创建数据库

    创建数据库有两种方式: 方式1-图形化界面创建: 1. 鼠标右击“数据库”,然后点击新建数据库 2.设置常规选项卡 2.1 给数据库命名,一般多个单词要用下划线连接,不建议用空格,如Test_DB. ...

  5. cnpm包管理

    C:\Users\Administrator>npm config set registry http://registry.npm.taobao.org vue 安装 npm install ...

  6. ASP.NET开发实战——(一)开篇-用VS创建一个ASP.NET Web程序

        本文是本系列文章第一篇,主要通过建立一个默认ASP.NET MVC项目来引出与ASP.NET MVC相关的功能,由于ASP.NET MVC一个简单的模板就具备了数据库操作.身份验证.输入数据校 ...

  7. AtCoder Grand Contest 037 简要题解

    从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...

  8. ECMAScript 初探 - 基础篇

    ECMAScript 语言的标准是由 Netscape.Sun.微软.Borland 等公司基于 JavaScript 和 JScript 锤炼.定义出来的. ECMAScript 仅仅是一个描述,定 ...

  9. Loj #3044. 「ZJOI2019」Minimax 搜索

    Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...

  10. W5500电路图

    W5500是韩国一款集成全硬件 TCP/IP 协议栈的嵌入式以太网控制器,W5500同时也是一颗工业级以太网控制芯片,最近发现我们国内也有和W5500 芯片一样芯片 介绍给大家 如下图: