优秀的编程知识分享平台

网站首页 > 技术文章 正文

Java 反射中,Class.forName和ClassLoader的区别

nanyue 2024-09-06 20:25:34 技术文章 6 ℃

一、区别

java 类装载过程分为 3 步:

加载 --> 链接 (1、校验, 2、准备, 3、解析) --> 初始化

1、加载 Jvm 把 class 文件字节码加载到内存中,并将这些静态数据装换成运行时数据区中方法区的类型数据,在运行时数据区堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区类数据的访问入口。

  • 注:方法区不仅仅是存放方法,它存放的是类的类型信息。

2、链接:执行下面的校验、准备和解析步骤,其中解析步骤是可选的 a:校验:检查加载的 class 文件的正确性和安全性 b:准备:为类变量分配存储空间并设置类变量初始值,类变量随类型信息存放在方法区中, 生命周期很长,使用不当和容易造成内存泄漏。

  • 注:类变量就是 static 变量;初始值指的是类变量类型的默认值而不是实际要赋的值
  • c:解析:jvm 将常量池内的符号引用转换为直接引用

3、初始化:执行类变量赋值和静态代码块

在了解了类装载过程之后我们继续比较二者区别:Classloder.loaderClass(String name) 其实该方法内部调用的是:Classloder. loadClass(name, false) 方法:Classloder. loadClass(String name, boolean resolve) a:参数 name 代表类的全限定类名 b:参数 resolve 代表是否解析,resolve 为 true 是解析该类

Class.forName(String name) 其实该方法内部调用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller)) 方法:Class.forName0(String name, boolean initialize, ClassLoader loader)

参数 name 代表全限定类名

参数 initialize 表示是否初始化该类,为 true 是初始化该类

参数 loader 对应的类加载器

两者最大的区别 Class.forName 得到的 class 是已经初始化完成的 Classloder.loaderClass 得到的 class 是还没有链接的

二、代码演示

java 中 class.forName()classLoader 都可用来对类进行加载。 class.forName() 前者除了将类的. class 文件加载到 jvm 中之外,还会对类进行解释,执行类中的 static 块。 而 classLoader 只干一件事情,就是将. class 文件加载到 jvm 中,不会执行 static 中的内容, 只有在 newInstance 才会去执行 static 块。 Class.forName(name, initialize, loader) 带参函数也可控制是否加载 static 块。并且只有调用了 newInstance() 方法采用调用构造函数,创建类的对象。

看下 Class.forName() 源码:

// Class.forName(String className)  这是1.8的源码
public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
// 注意第二个参数,是指Class被loading后是不是必须被初始化。 不初始化就是不执行static的代码即静态代码

接着测试代码证明上面的结论是 OK 的,如下:

package com.reflect;

public class Line {
    static {
        System.out.println("静态代码块执行: loading line");
    }
}
package com.reflect;

public class Point {
    static {
        System.out.println("静态代码块执行: loading point");
    }
}
package com.reflect;

public class ClassloaderAndForNameTest {
    public static void main(String[] args) {
        String wholeNameLine = "com.reflect.Line";
        String wholeNamePoint = "com.reflect.Point";
        System.out.println("下面是测试Classloader的效果");
        testClassloader(wholeNameLine, wholeNamePoint);
        System.out.println("----------------------------------");
        System.out.println("下面是测试Class.forName的效果");
        testForName(wholeNameLine, wholeNamePoint);
    }

    /**
     * classloader
     * @param wholeNameLine
     * @param wholeNamePoint
     */
    private static void testClassloader(String wholeNameLine, String wholeNamePoint) {
        Class<?> line;
        Class<?> point;
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        try {
            line = loader.loadClass(wholeNameLine);
            point = loader.loadClass(wholeNamePoint);
            System.out.println("line " + line.getName());
            System.out.println("point " + point.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * Class.forName
     * @param wholeNameLine
     * @param wholeNamePoint
     */
    private static void testForName(String wholeNameLine, String wholeNamePoint) {
        try {
            Class<?> line = Class.forName(wholeNameLine);
            Class<?> point = Class.forName(wholeNamePoint);
            System.out.println("line   " + line.getName());
            System.out.println("point   " + point.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出结果:

下面是测试Classloader的效果
line com.reflect.Line
point com.reflect.Point
----------------------------------
下面是测试Class.forName的效果
静态代码块执行: loading line
静态代码块执行: loading point
line   com.reflect.Line
point   com.reflect.Point

备注:

根据运行结果,可以看到,classloader 并没有执行静态代码块,如开头的理论所说。

而下面的 Class.forName 则是夹在完之后,就里面执行了静态代码块,可以看到,2 个类,line 和 point 的静态代码块执行结果是一起的,然后才是各自的打印结果。

也说明上面理论是 OK 的。

接下来修改下 Line 的代码,添加了几个静态的方法和变量。

package com.reflect;

public class Line {
    static {
        System.out.println("静态代码块执行: loading line");
    }

    public static String s = getString();

    private static String getString() {
        System.out.println("给静态变量赋值的静态方法执行:loading line");
        return "mask";
    }

    public static void test() {
        System.out.println("普通静态方法执行:loading line");
    }

    {
        System.out.println("普通代码块");
    }

    public Line() {
        System.out.println("构造方法执行");
    }

}

可以看到,除了原来的简单的一个静态代码块以外,我又添加了构造方法,静态方法,以及静态变量,且,静态变量被一个静态方法赋值。

然后,看执行结果。

下面是测试Classloader的效果
line com.reflect.Line
point com.reflect.Point
----------------------------------
下面是测试Class.forName的效果
静态代码块执行: loading line
给静态变量赋值的静态方法执行:loading line
静态代码块执行: loading point
line   com.reflect.Line
point   com.reflect.Point

除了,静态代码块的执行外,竟然还有一个静态方法被执行,就是给静态变量赋值的静态方法被执行了。

三、应用场景

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

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

以 MySQL 的驱动为例解释:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
    // ~ Static fields/initializers  
    // ---------------------------------------------  
  
    //  
    // Register ourselves with the DriverManager  
    //  
    static {  
        try {  
            java.sql.DriverManager.registerDriver(new Driver());  
        } catch (SQLException E) {  
            throw new RuntimeException("Can't register driver!");  
        }  
    }  
  
    // ~ Constructors  
    // -----------------------------------------------------------  
  
    /** 
     * 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() 的原因了。

作者:老周聊架构

来源:稀土掘金

链接:https://juejin.cn/post/6952750364936372237

版权声明: 内容来源网络,仅供分享学习,版权归原创作者所有,除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除表示歉意。谢谢!

最近发表
标签列表