Class.forName 和 ClassLoader的区别

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

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

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    return forName(className, true, VMStack.getCallingClassLoader());
}

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

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

/**
* Returns the {@code 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 {@code 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
* {@code loader} is null, the class is loaded through the bootstrap
* class loader.  The class is initialized only if the
* {@code initialize} parameter is {@code true} and if it has
* not been initialized earlier.
*
* <p> If {@code 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 {@code name}. Therefore, this method cannot be used to
* obtain any of the {@code Class} objects representing primitive
* types or void.
*
* <p> If {@code name} denotes an array class, the component type of
* the array class is loaded but not initialized.
*
* <p> For example, in an instance method the expression:
*
* <blockquote>
*  {@code Class.forName("Foo")}
* </blockquote>
*
* is equivalent to:
*
* <blockquote>
*  {@code Class.forName("Foo", true, this.getClass().getClassLoader())}
* </blockquote>
*
* Note that this method throws errors related to loading, linking or
* initializing as specified in Sections 12.2, 12.3 and 12.4 of <em>The
* Java Language Specification</em>.
* Note that this method does not check whether the requested class
* is accessible to its caller.
*
* <p> If the {@code loader} is {@code null}, and a security
* manager is present, and the caller's class loader is not null, then this
* method calls the security manager's {@code checkPermission} method
* with a {@code RuntimePermission("getClassLoader")} permission to
* ensure it's ok to access the bootstrap class loader.
*
* @param name       fully qualified name of the desired class
* @param initialize whether the class must be initialized
* @param loader     class loader from which the class must be loaded
* @return           class object representing the desired class
*
* @exception LinkageError if the linkage fails
* @exception ExceptionInInitializerError if the initialization provoked
*            by this method fails
* @exception ClassNotFoundException if the class cannot be located by
*            the specified class loader
*
* @see       java.lang.Class#forName(String)
 * @see       java.lang.ClassLoader
 * @since     1.2
 */
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    if (loader == null) {
        loader = BootClassLoader.getInstance();
    }
    Class<?> result;
    try {
        result = classForName(name, initialize, loader);
    } catch (ClassNotFoundException e) {
        Throwable cause = e.getCause();
        if (cause instanceof LinkageError) {
            throw (LinkageError) cause;
        }
        throw e;
    }
    return result;
}

源码中的注释只摘取了一部分,其中对参数 initialize 的描述是:if {@code true} the class will be initialized. 意思就是说:如果参数为 true,则加载的类将会被初始化。

举例:

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

public class ClassForName {
    // 静态代码块
    static {
        System.out.println("执行了静态代码块");
    }
    // 静态变量
    private static String staticFiled = staticMethod();
    // 赋值静态变量的静态方法
    public static String staticMethod() {
        System.out.println("执行了静态方法");
        return "给静态字段赋值了";
    }
}

测试方法:

public class MyTest {
    @Test
    public void test() {
        try {
            class.forName("com.test.mytest.ClassForName");
            System.out.println("#####分隔符(上面是Class.forName的加载过程, 下面是ClassLoader的加载过程)#####");
            ClassLoader.getSystemClassLoader().loadClass("com.test.mytest.ClassForName");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

执行了静态代码块
执行了静态方法
#####分隔符(上面是Class.forName的加载过程,下面是ClassLoader的加载过程)#####

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

应用场景

在我们熟悉的 Spring 框架中的 IOC 的实现就是使用的 ClassLoader。

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

以 MySQL 的驱动为例解释:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // Static fields/initiallzers
    // --------------------------------
    // Register ourselves with the DriverManager
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException e) {
            throw new RuntimeException("Can't regoster driver!");
        }
    }
    // Constuctors
    /**
     * Construct a new driver and register it with DriverManager
     * @throws SQLException if a database error occurs.
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

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

来源:http://t.cn/AiQQ7dwi

给TA打赏
共{{data.count}}人
人已打赏
技术教程投稿

17个方面,综合对比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四个分布式消息队列

2023-4-12 18:04:28

技术教程投稿

Redis从入门到精通,至少要看看这篇!

2023-4-13 13:23:47

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索