Class.forName和ClassLoader有什么区别

本文详细探讨了Java中的Class.forName()方法和ClassLoader之间的区别。Class.forName()不仅加载类,还会初始化,而ClassLoader仅加载类。在JDBC中,通常使用Class.forName()来加载数据库驱动,因为驱动类在静态代码块中注册到DriverManager。了解这些差异有助于深入理解类加载机制。
摘要由CSDN通过智能技术生成

本文来说下Class.forName 和 ClassLoader 有什么区别


概述

在 java 中 Class.forName() 和 ClassLoader 都可以对类进行加载。ClassLoader 就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到 JVM 中。Class.forName() 方法实际上也是调用的 CLassLoader 来实现的

Class.forName(String className);这个方法的源码是:

/**
Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
For example, the following code fragment returns the runtime Class descriptor for the class named java.lang.Thread:
Class t = Class.forName("java.lang.Thread")
A call to forName("X") causes the class named X to be initialized.
Params:
className – the fully qualified name of the desired class.
Returns:
the Class object for the class with the specified name.
Throws:
LinkageError – if the linkage fails
ExceptionInInitializerError – if the initialization provoked by this method fails
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);
}

最后调用的方法是 forName0 这个方法,在这个 forName0 方法中的第二个参数被默认设置为了 true,这个参数代表是否对加载的类进行初始化,设置为 true 时会类进行初始化,代表会执行类中的静态代码块,以及对静态变量的赋值等操作。

也可以调用 Class.forName(String name, boolean initialize,ClassLoader loader) 方法来手动选择在加载类的时候是否要对类进行初始化。Class.forName(String name, boolean initialize,ClassLoader loader) 的源码如下:


/**
Returns the Class object associated with the class or interface with the given string name, using the given class loader. Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface. The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader. The class is initialized only if the initialize parameter is true and if it has not been initialized earlier.
If name denotes a primitive type or void, an attempt will be made to locate a user-defined class in the unnamed package whose name is name. Therefore, this method cannot be used to obtain any of the Class objects representing primitive types or void.
If name denotes an array class, the component type of the array class is loaded but not initialized.
For example, in an instance method the expression:
Class.forName("Foo")
is equivalent to:
Class.forName("Foo", true, this.getClass().getClassLoader())
Note that this method throws errors related to loading, linking or initializing as specified in Sections 12.2, 12.3 and 12.4 of The Java Language Specification. Note that this method does not check whether the requested class is accessible to its caller.
If the loader is null, and a security manager is present, and the caller's class loader is not null, then this method calls the security manager's checkPermission method with a RuntimePermission("getClassLoader") permission to ensure it's ok to access the bootstrap class loader.
Params:
name – fully qualified name of the desired class
initialize – if true the class will be initialized. See Section 12.4 of The Java Language Specification.
loader – class loader from which the class must be loaded
Returns:
class object representing the desired class
Throws:
LinkageError – if the linkage fails
ExceptionInInitializerError – if the initialization provoked by this method fails
ClassNotFoundException – if the class cannot be located by the specified class loader
Since:
1.2
See Also:
forName(String), 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);
  }

实例测试

下面还是举例来说明结果吧:一个含有静态代码块、静态变量、赋值给静态变量的静态方法的类。

package cn.yiyiyun.pre.app.handle.test;

public class ClassForName {

    //静态代码块
    static {
        System.out.println("执行了静态代码块");
    }

    //静态变量
    private static String staticFiled =staticMethod();

     //赋值静态变量的静态方法
     public static String staticMethod() {
         System.out.println("执行了静态方法");
         return "给静态字段赋值了";
     }

}

测试方法:

package cn.yiyiyun.pre.app.handle.test;

public class Main {

    public static void main(String[] args) {

        try {
            Class.forName("cn.yiyiyun.pre.app.handle.test.ClassForName");
            System.out.println("#########分割符(上面是class.forName的加载过程,下面是ClassLoader的加载过程)##########");
            ClassLoader.getSystemClassLoader().loadClass("cn.yiyiyun.pre.app.handle.test.ClassForName");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

程序结果

在这里插入图片描述

根据运行结果得出 Class.forName 加载类是将类进了初始化,而 ClassLoader 的 loadClass 并没有对类进行初始化,只是把类加载到了虚拟机中


应用场景

在我们熟悉的 Spring 框架中的 IOC 的实现就是使用的 ClassLoader。而在我们使用 JDBC 时通常是使用 Class.forName() 方法来加载数据库连接驱动。这是因为在 JDBC 规范中明确要求 Driver(数据库驱动)类必须向 DriverManager 注册自己。

以 MySQL 的驱动为例解释:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

我们看到 Driver 注册到 DriverManager 中的操作写在了静态代码块中,这就是为什么在写 JDBC 时使用 Class.forName() 的原因了。


本文小结

本文详细介绍了Class.forName和ClassLoader之间的区别的和联系,对于更加深入的理解类加载相关的知识与内容是十分有好处的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值