Java学习笔记:反射class.forName和ClassLoader的区别记录

反射class.forName和ClassLoader的区别记录

first

相同:
java中class.forName()和classLoader都可用来对类进行加载。

不同:
1)class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块,还会执行给静态变量赋值的静态方法

2)classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

注:Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,才会创建类的对象,

sec

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()的原因了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值