驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
Class.forName()和 newInstance() 区别
/  

Class.forName()和 newInstance() 区别

有句话说得好:基础不好,代码写不好。(0.0 这话我编的...)

之前对于提到的几个 API 没有很好的理解,趁着今天在使用,所以梳理一下。

  • 静态方法:Class.forName(“xxx”) 的作用
  • 非静态方法 clazz.newInstance() 的作用
  • 直接通过 new的区别

Class.forName("xxx")

作用

老规矩,先上代码:

Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");

从上述代码可以看出3点:

  1. forName 是一个Class的静态方法,通过Class 直接调用。
  2. forName 中需要给出一个 全路径的包+类
  3. forName返回值是一个 Class对象。

这个 API 有什么作用了?

如果要了解静态方法Class.forName(“xxx”) 的作用,需要了解JVM 中类的加载机制,这里不展开讲。

简单而言就是要使用某一个类,那么必须先从 .class 的二进制文件加载到JVM中。forname 就是起到的就是这个作用。

这个时候你可能会想,我们平时在使用的时候是直接 new 的,没有通过 forName,那是不是就没有加载类了?

请保留这个疑问,继续往下看,后续的 new 的部分会解答这个疑问。

扩展

我们通过代码测试下,假如有一个RefPerson 类,如下所示:

@Data
public class RefPerson {

    private String name;

    private Integer age;

    static {
        System.out.println("RefPerson static init...");
    }

    public RefPerson() {
        System.out.println("RefPerson init...");
    }
}

核心测试代码:

    @Test
    public void refTest0() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");
        System.out.println(clazz);
    }

其输出如下所示:

RefPerson static init...
class club.hicode.daydayup.reflect.RefPerson

大家发现没有,输出的是2行,这个分别是什么?

  1. 第一行RefPerson static init... 是因为RefPerson中有一个静态代码块,该语句是在静态代码块中被执行的。由此可以发现:forName的时候会执行静态代码块中的内容,这个也是很多时候我们在写 JDBC 的时候会用到的,比如如下代码:
 Class.forName("com.mysql.jdbc.Driver");

​ 这个代码其核心执行的其实是 com.mysql.jdbc.Driver这个类的静态代码块中的代码:

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!");
        }
    }
}

就是 static 这个里面的代码。

  1. 第二行输出的就是加载的类。

clazz.newInstance()

作用

该方法通常会和Class.forName()搭配使用,如下所示:

    @Test
    public void refTest3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");
        RefPerson refPerson = (RefPerson) clazz.newInstance();
    }

通过加载到 JVM中的 Class 对象调用newInstance() 进行具体实例的初始化。

扩展

上述代码的输出是什么?

RefPerson static init...
RefPerson init...
  1. 第一行是 forName 加载类的时候,调用的静态代码块中的输出的。
  2. 第二行是RefPerson无参构造函数中输出的。

这个地方有一个细节:

clazz.newInstance()其实会反射调用clazz对应类的无参构造函数,如果没有无参构造函数那么就会出报错:ClassNotFoundException

这个地方再扩展个内容,如果当前代码不具有无参的构造函数,又想反射调用,怎么办?

缺失无参构造函数的时候初始化

假如RefPerson 改为如下所示:

@Data
public class RefPerson {

    private String name;

    private Integer age;

    static {
        System.out.println("RefPerson static init...");
    }

    // 这有这个有参的构造函数
    public RefPerson(String name) {
        this.name = name;
    }
}
  1. 引入如下 xml
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>2.6</version>
</dependency>
  1. 通过如下代码使用
    @Test
    public void refTest4() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");
        // 这个地方
        org.objenesis.Objenesis objenesis = new org.objenesis.ObjenesisStd(true);
        RefPerson refPerson = (RefPerson) objenesis.newInstance(clazz);
        System.out.println(refPerson);
    }

其实如果已经引入Spring那么它自身就已经具有了

    @Test
    public void refTest5() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");
        org.springframework.objenesis.Objenesis objenesis = new org.springframework.objenesis.ObjenesisStd(true);
        RefPerson refPerson = (RefPerson) objenesis.newInstance(clazz);
        System.out.println(refPerson);
    }

输出为:

RefPerson static init...
RefPerson(name=null, age=null)

new 使用

其实 new 是当明确了类的时候使用的,其作用等价于:

Class.forName("xxx.xxx").newInstance();

new 的时候也会触发2个操作:

  1. 加载 Class 到方法区
  2. 创建对象

总结

这3个东西,分别何时使用了?

  1. Class.forName 的一个典型使用就是 在静态代码块中 注入 SQL 驱动,在上面文章已经讲过了。
  2. clazz.newInstance 是通过反射创建对象,是实现IOC、反射、面对接口编程和依赖倒置等技术方法的必然选择,通常框架会使用,使我们代码更加灵活。
  3. new 明确了使用类的时候,直接通过 new,最简单、方便、快捷。缺点就是不够灵活。

您有任何建议和意见,请Email联系: hicode_club@163.com

转载请保留出处 HiCode 俱乐部

骐骥一跃,不能十步。驽马十驾,功在不舍。