Java提供了两种方式让我们在运行时识别对象和类的信息:

一种是传统的RTTI(Run-Time Type Identification),它假定我们在编译时已经知道了所有的类型信息;

另一种是反射机制,它允许我们在运行时发现和使用类的信息。反射使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)。

Java有两种加载class到JVM中的方式:

Class.forName("className")

ClassLoader.laodClass("className")

下面一起看一下两者的区别:


01

JVM类加载的过程

JVM类加载的过程可以分为五个步骤:加载 -> 验证 -> 准备 -> 解析 -> 初始化,具体如下图所示:Class.forName 和 ClassLoader 有什么区别?_类加载器Java是如何加载到JVM里面然后被识别使用的呢,其实主要分为三个步骤:

(1)装载:(loading)找到class对应的字节码文件。

(2)连接:(linking)将对应的字节码文件读入到JVM中。

(3)初始化:(initializing)对class做相应的初始化动作。

02

Class.forName 和 ClassLoader 源码解析

ClassLoader.laodClass("className")

该方法实际调用的是ClassLoader.loadClass(name, false)方法:

(1)参数:name,表示需要加载的类的名称;

(2)参数:false,表示这个类加载以后是否需要去连接。

ClassLoader 就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到 JVM 中。



Class.forName("className")

方法实际调用的是:Class.forName(className, true,ClassLoader.getCallerClassLoader())方法

(1)参数:className,表示需要加载的类的名称;

(2)参数:true,表示是否对class进行初始化;

(3)参数:classLoader,表示对应的类加载器。Class.forName() 方法实际上也是调用的 CLassLoader 来实现的。Class.forName()的源码:



/*
* @param className the fully qualified name of the desired class.
* @return the {@code Class} object for the class with the
* specified name.
* @exception LinkageError if the linkage fails
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails
* @exception ClassNotFoundException if the class cannot be located
*/
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

/** 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;

03

对比

3.1 相同点

Class.forName 和 ClassLoader 都可用来对类进行加载。

3.2 区别点


  • Class.forName加载并初始化了一个类
  • ClassLoader仅加载了类,不会执行static中的内容,只有在newInstance才会去执行static块。

3.3 小结

如果程序需要初始化,就必须用Class.forName(name)了。例如要求加载时类的静态变量被初始化或静态块里的代码被执行就只能用 forName,而用 loadClass 只有等创建类实例时才会进行这些初始化。


04

应用场景

JDBC 时通常是使用 Class.forName() 方法来加载数据库连接驱动

因为在 JDBC 规范中明确要求 Driver(数据库驱动)类必须向 DriverManager 注册自己。以 MySQL 的驱动为例:

Class.forName 和 ClassLoader 有什么区别?_字节码_02

综合上面的分析,可知JDBC需要使用Class.forName来实现 静态代码块中Driver 注册到 DriverManager 中的操作。

Class.forName 和 ClassLoader 有什么区别?_字节码_03