有句话说得好:基础不好,代码写不好。(0.0 这话我编的...)
之前对于提到的几个 API 没有很好的理解,趁着今天在使用,所以梳理一下。
Class.forName(“xxx”)
的作用clazz.newInstance()
的作用new
的区别老规矩,先上代码:
Class<?> clazz = Class.forName("club.hicode.daydayup.reflect.RefPerson");
从上述代码可以看出3点:
forName
是一个Class
的静态方法,通过Class
直接调用。forName
中需要给出一个 全路径的包+类
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行,这个分别是什么?
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 这个里面的代码。
该方法通常会和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...
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;
}
}
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.6</version>
</dependency>
@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 是当明确了类的时候使用的,其作用等价于:
Class.forName("xxx.xxx").newInstance();
new 的时候也会触发2个操作:
这3个东西,分别何时使用了?
您有任何建议和意见,请Email联系: hicode_club@163.com
转载请保留出处 HiCode 俱乐部